﻿using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
using OpenCvSharp;
using static System.Windows.Forms.LinkLabel;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;

public class ImageBlockProcessor
{/// <summary>
 /// 保守型二值化处理（最小化失真）
 /// </summary>
    public static Mat ConvertToBinaryConservative(Mat input)
    {
        // 1. 转为灰度图（保持原始亮度分布）
        using Mat gray = new Mat();
        Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
 
        // 2. 极轻度模糊（仅消除高频噪点）
        using Mat blurred = new Mat();
        Cv2.GaussianBlur(gray, blurred, new Size(3, 3), 0.5);
     
        // 3. 动态阈值处理（自动选择最优方法）
        using Mat binary = new Mat();
        double contrast = Cv2.Mean(blurred).Val0;
    
        // 根据图像对比度自动选择策略
        if (contrast > 100) // 高对比度图像
        {
            Cv2.Threshold(blurred, binary, 0, 255,
                ThresholdTypes.Binary | ThresholdTypes.Otsu);
        }
        else // 低对比度图像
        {
            Cv2.AdaptiveThreshold(
                blurred,
                binary,
                255,
                AdaptiveThresholdTypes.MeanC,
                ThresholdTypes.Binary,
                blockSize: 21,  // 大窗口保证平滑
                c: 8             // 高C值防止过度二值化
            );
        }

        return binary.Clone();
    }
    public static List<Rect> ProcessImageWithBlockNumbersV(Mat inputImage, out Mat reMat)
    {
        // 1. 反相处理
        using Mat inverted = InvertImage(inputImage);

        // 2. 水平膨胀（垂直方向不膨胀）
        using Mat dilated = DilateImage(inverted, 15, 1);
        //dilated.SaveImage("R:\\dilated.JPG");

        // 3. 连通区域分块
        List<Rect> blocks = GetConnectedBlocks(dilated);

        // 4. 对每个分块进行垂直分行处理
        List<Rect> allLines = new List<Rect>();
        foreach (var block in blocks)
        {
            // 提取当前块ROI
            using Mat blockROI = new Mat(inputImage, block);

            // 对当前块进行垂直分行（复用相同算法）
            using Mat blockInverted = InvertImage(blockROI);
            using Mat blockDilated = DilateImage(blockInverted, 3, 20); // 垂直膨胀
                                                                        //blockDilated.SaveImage("R:\\BL.JPG");
                                                                        //  var inblockDilated = InvertImage(blockDilated);
            VerticalLineSplitter vls = new VerticalLineSplitter();
            List<Rect> Cs = vls.SplitBlockIntoLinesA(InvertImage(blockDilated));
            #region
            //ToDo 将间距小于指定值的小RECT 进行合并（水平方向）
            const int maxHorizontalGap = 3; // 定义最大允许的水平间距阈值(像素)
            if (Cs.Count > 1)
            {
                // 按X坐标排序
                Cs.Sort((a, b) => a.X.CompareTo(b.X));

                List<Rect> mergedRects = new List<Rect>();
                Rect currentRect = Cs[0];

                for (int i = 1; i < Cs.Count; i++)
                {
                    // 计算当前矩形与下一个矩形的水平间距
                    int gap = Cs[i].X - (currentRect.X + currentRect.Width);
                    
                    bool HB = (Cs[i].Width < 10 );
                    if(i < Cs.Count-1)
                        HB = (Cs[i].Width < 10|| Cs[i+1].Width < 10);
                    if ( gap <= maxHorizontalGap  && HB)
                    {
                        // 合并矩形: 扩展当前矩形的宽度以包含下一个矩形
                        int newX = Math.Min(currentRect.X, Cs[i].X);
                        int newRight = Math.Max(currentRect.X + currentRect.Width, Cs[i].X + Cs[i].Width);
                        int newWidth = newRight - newX;

                        // 合并后的高度取两者中较大的，或者根据需要调整
                        int newHeight = Math.Max(currentRect.Height, Cs[i].Height);
                        int newY = Math.Min(currentRect.Y, Cs[i].Y);

                        currentRect = new Rect(newX, newY, newWidth, newHeight);
                    }
                    else
                    {
                        mergedRects.Add(currentRect);
                        currentRect = Cs[i];
                    }
                }
                mergedRects.Add(currentRect);

                // 使用合并后的矩形列表
                Cs = mergedRects;

               


            }
            #endregion
            // 将局部坐标转换为全局坐标
            foreach (var line in Cs)
            {
                allLines.Add(new Rect(
                    block.X + line.X,
                    block.Y + line.Y,
                    line.Width,
                    line.Height));
            }
        }
        // 5. 编号可视化（显示最终分行结果）

        var FG = SplitWideRects(allLines, 2.1, 0.9);

        reMat = DrawBlockNumbers(inputImage, FG);
 
        return FG;

    }
    /// <summary>
    /// 将过宽的矩形分割为接近平均宽度的多个矩形
    /// </summary>
    /// <param name="rects">待处理的矩形列表</param>
    /// <param name="splitMultiplier">分割倍数阈值(默认2.5倍平均宽度时分割)</param>
    /// <param name="minSplitRatio">最小分割比例(防止过度分割，默认0.6)</param>
    /// <returns>处理后的矩形列表</returns>
    public static List<Rect> SplitWideRects(List<Rect> rects, double splitMultiplier = 2.5, double minSplitRatio = 0.6)
    {
        if (rects == null || rects.Count == 0)
            return rects;

        // 计算平均宽度
        double avgWidth = rects.Average(r => r.Width);
        double splitThreshold = avgWidth * splitMultiplier;
        double minSplitWidth = avgWidth * minSplitRatio;

        List<Rect> result = new List<Rect>();

        foreach (var rect in rects)
        {
            if (rect.Width > splitThreshold)
            {
                // 计算分割数量
                int splitCount = Math.Max(1, (int)(rect.Width / minSplitWidth));

                // 计算每个分割后的宽度
                int splitWidth = rect.Width / splitCount;

                // 执行分割
                for (int i = 0; i < splitCount; i++)
                {
                    int width = (i == splitCount - 1)
                        ? rect.Width - i * splitWidth  // 最后一个矩形取剩余宽度
                        : splitWidth;

                    result.Add(new Rect(
                        rect.X + i * splitWidth,
                        rect.Y,
                        width,
                        rect.Height));
                }
            }
            else
            {
                result.Add(rect);
            }
        }

        return result;
    }
    public static List<Rect> ProcessImageWithBlockNumbers(Mat inputImage,out Mat reMat)
    {
        // 1. 反相处理
        using Mat inverted = InvertImage(inputImage);

        // 2. 水平膨胀（垂直方向不膨胀）
        using Mat dilated = DilateImage(inverted, 15, 1);
        dilated.SaveImage("R:\\dilated.JPG");

        // 3. 连通区域分块
        List<Rect> blocks = GetConnectedBlocks(dilated);

        // 4. 对每个分块进行垂直分行处理
        List<Rect> allLines = new List<Rect>();
        foreach (var block in blocks)
        {
            // 提取当前块ROI
            using Mat blockROI = new Mat(inputImage, block);

            // 对当前块进行垂直分行（复用相同算法）
            using Mat blockInverted = InvertImage(blockROI);
            using Mat blockDilated = DilateImage(blockInverted, 20, 1); // 垂直膨胀
           blockDilated.SaveImage("R:\\BL.JPG");
          //  var inblockDilated = InvertImage(blockDilated);
            VerticalLineSplitter vls= new VerticalLineSplitter();
            List<Rect> lines = vls.SplitBlockIntoLines(InvertImage(blockDilated));
           
            // 将局部坐标转换为全局坐标
            foreach (var line in lines)
            {
                allLines.Add(new Rect(
                    block.X + line.X,
                    block.Y + line.Y,
                    line.Width,
                    line.Height));
            }
        }
         // allLines = FilterBlocks(allLines); //对分块进行合并 因为要重新编号
        // 5. 编号可视化（显示最终分行结果）
        reMat = DrawBlockNumbers(inputImage, allLines);

        return allLines;

    }




    /// <summary>
    /// 图像反相处理
    /// </summary>
    public  static Mat InvertImage(Mat input)
    {
        Mat output = new Mat();
        Cv2.BitwiseNot(input, output);
        return output;
    }

    public static Mat Erode(Mat src )
    {
        Mat kernel = Cv2.GetStructuringElement(
            MorphShapes.Rect,
            new Size(1, 1));  // 3x3矩形核

        // 4. 执行腐蚀操作
        Mat eroded = new Mat();
        Cv2.Erode(src, eroded, kernel);
        return eroded;
    }



    /// <summary>
    /// 图像膨胀处理
    /// </summary>
    public  static Mat DilateImage(Mat input, int width, int height)
    {
        Mat output = new Mat();
        Mat kernel = Cv2.GetStructuringElement(
            MorphShapes.Rect,
            new Size(width, height));
        Cv2.Dilate(input, output, kernel);
        return output;
    }

    /// <summary>
    /// 获取连通区域分块
    /// </summary>
    public  static List<Rect> GetConnectedBlocks(Mat binaryImage)
    {
        // 确保是单通道二值图像
          Mat processed = new Mat();
        if (binaryImage.Channels() > 1)
        {
            Cv2.CvtColor(binaryImage, processed, ColorConversionCodes.BGR2GRAY);
        }
        else
        {
            processed = binaryImage.Clone();
        }
       // processed = InvertImage(processed);

        // 连通区域分析
        Mat labels = new Mat();
        Mat stats = new Mat();
        Mat centroids = new Mat();
        int numLabels = Cv2.ConnectedComponentsWithStats(processed, labels, stats, centroids);

        // 提取区块信息（跳过背景0）
        List<Rect> blocks = new List<Rect>();
        for (int i = 1; i < numLabels; i++)
        {
            blocks.Add(new Rect(
                stats.At<int>(i, 0), // x
                stats.At<int>(i, 1), // y
                stats.At<int>(i, 2), // width
                stats.At<int>(i, 3)  // height
            ));
        }

        // 按位置排序（从上到下，从左到右）
        return blocks.OrderBy(b => b.Top / 40)  // 垂直分组容差
                    .ThenBy(b => b.Left)
                    .ToList();
    }
    /// <summary>
    /// 绘制区块编号（简化颜色版）
    /// </summary>
    public  static Mat DrawBlockNumbers(Mat background, List<Rect> blocks, int groupTolerance = 20)
    {
        // 1. 准备结果图像
        Mat result = background.Channels() == 1
            ? background.CvtColor(ColorConversionCodes.GRAY2BGR)
            : background.Clone();

        if (blocks == null || !blocks.Any())
        {
            Cv2.PutText(result, "No blocks detected",
                new Point(10, 30),
                HersheyFonts.HersheySimplex, 0.8, Scalar.Red, 2);
            return result;
        }

        // 2. 简化颜色方案（预定义颜色数组）
        Scalar[] palette = {
        Scalar.FromRgb(255, 100, 100), // 红
        Scalar.FromRgb(100, 255, 100), // 绿
        Scalar.FromRgb(100, 100, 255), // 蓝
        Scalar.FromRgb(255, 255, 100), // 黄
        Scalar.FromRgb(255, 100, 255), // 粉
        Scalar.FromRgb(100, 255, 255)  // 青
    };

        // 3. 绘制区块
        for (int i = 0; i < blocks.Count; i++)
        {
            Rect block = blocks[i];
            int groupKey = block.Y / groupTolerance;

            // 3.1 取模选择颜色
            Scalar color = palette[groupKey % palette.Length];

            // 3.2 绘制区块背景（半透明）
            using (Mat overlay = result.Clone())
            {
                Cv2.Rectangle(overlay, block, color, -1);
                Cv2.AddWeighted(overlay, 0.2, result, 0.8, 0, result);
            }

            // 3.3 绘制边框
            Cv2.Rectangle(result, block, color, 2);

            // 3.4 绘制编号（自适应大小）
            double fontSize = Math.Clamp(block.Height / 30, 0.5, 20);
            Point textPos = new Point(block.X + 5, block.Y + block.Height - 5);

            // 白字黑边保证可读性
            Cv2.PutText(result, $"{i + 1}", textPos + new Point(1, 1),
                HersheyFonts.HersheySimplex, fontSize, Scalar.Black, 2);
            Cv2.PutText(result, $"{i + 1}", textPos,
                HersheyFonts.HersheySimplex, fontSize, Scalar.White, 1);
        }

        // 4. 添加统计信息
        int groupCount = blocks.GroupBy(b => b.Y / groupTolerance).Count();
        Cv2.PutText(result, $"Blocks: {blocks.Count} | Groups: {groupCount}",
            new Point(10, 30),
            HersheyFonts.HersheySimplex, 0.7, Scalar.Yellow, 2);

        return result;
    }


    public static List<Rect> FilterBlocksHeight(List<Rect> blocks)
    {
        if (blocks == null || blocks.Count == 0)
            return new List<Rect>();

        // 步骤1: 初始过滤（使用原始blocks的中位数）
        var medianHeight = blocks.Select(b => b.Height).OrderBy(h => h).ElementAt(blocks.Count / 2);
        
        // 过滤参数
        double minHeightRatio = 0.8;
        double maxHeightRatio = 1.3;
     

        // 执行初始过滤
        var filtered = blocks.Where(b =>
        {
            double heightRatio =(double ) b.Height / (double)medianHeight;


            return (heightRatio >= minHeightRatio &&
                   heightRatio <= maxHeightRatio );
                  
        }).ToList();

        // 步骤2: 处理过大块（使用过滤后数据的中位数）
        if (filtered.Count == 0)
            return filtered;  // 无剩余块时直接返回

        // 计算过滤后块的中位高度
        var filteredHeights = filtered.Select(b => b.Height).OrderBy(h => h).ToList();
        double medianHeightFiltered = filteredHeights[filteredHeights.Count / 2];

        // 被过滤的块（原始列表与过滤后列表的差集）
        var rejected = blocks.Except(filtered).ToList();
        var newBlocks = new List<Rect>();

        foreach (var block in rejected)
        {
            double ratio = (double)block.Height / (double)medianHeightFiltered;

            // 检查是否为2倍高度块（1.7-2.3倍区间）
            if (ratio >= 1.7 && ratio <= 2.3)
            {
                double segmentHeight = block.Height / 2;
                newBlocks.Add(new Rect(block.X, block.Y, block.Width, (int)segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)(block.Y + segmentHeight), block.Width, (int)segmentHeight));
            }
            // 检查是否为3倍高度块（2.55-3.45倍区间）
            else if (ratio >= 2.55 && ratio <= 3.45)
            {
                double segmentHeight = block.Height / 3;
                newBlocks.Add(new Rect(block.X, block.Y, block.Width, (int)segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)(block.Y + segmentHeight), block.Width, (int)segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)(block.Y + 2 * segmentHeight), block.Width, (int)segmentHeight));
            }
        }

        // 步骤3: 合并结果
        filtered.AddRange(newBlocks);
        return filtered;
    }
    public static List<Rect> FilterBlocks(List<Rect> blocks)
    {
        if (blocks == null || blocks.Count == 0)
            return new List<Rect>();

        // 步骤1: 初始过滤（使用原始blocks的中位数）
        var medianHeight = blocks.Select(b => b.Height).OrderBy(h => h).ElementAt(blocks.Count / 2);
        var medianWidth = blocks.Select(b => b.Width).OrderBy(w => w).ElementAt(blocks.Count / 2);
        var medianArea = blocks.Select(b => b.Width * b.Height).OrderBy(a => a).ElementAt(blocks.Count / 2);

        // 过滤参数
        double minHeightRatio = 0.8;
        double maxHeightRatio = 1.3;
        double minWidthRatio = 0.8;
        double maxWidthRatio = 1.5;
        double minAreaRatio = 0.3;

        // 执行初始过滤
        var filtered = blocks.Where(b =>
        {
            double heightRatio = b.Height / medianHeight;
            double widthRatio = b.Width / medianWidth;
            double areaRatio = (b.Width * b.Height) / medianArea;

            return ( heightRatio >= minHeightRatio &&
                   heightRatio <= maxHeightRatio &&
                   widthRatio >= minWidthRatio &&
                   widthRatio <= maxWidthRatio &&
                   areaRatio >= minAreaRatio);
        }).ToList();

        // 步骤2: 处理过大块（使用过滤后数据的中位数）
        if (filtered.Count == 0)
            return filtered;  // 无剩余块时直接返回

        // 计算过滤后块的中位高度
        var filteredHeights = filtered.Select(b => b.Height).OrderBy(h => h).ToList();
        double medianHeightFiltered = filteredHeights[filteredHeights.Count / 2];

        // 被过滤的块（原始列表与过滤后列表的差集）
        var rejected = blocks.Except(filtered).ToList();
        var newBlocks = new List<Rect>();

        foreach (var block in rejected)
        {
            double ratio = block.Height / medianHeightFiltered;

            // 检查是否为2倍高度块（1.7-2.3倍区间）
            if (ratio >= 1.7 && ratio <= 2.3)
            {
                double segmentHeight = block.Height / 2;
                newBlocks.Add(new Rect(block.X, block.Y, block.Width,(int ) segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)(block.Y + segmentHeight), block.Width, (int)segmentHeight));
            }
            // 检查是否为3倍高度块（2.55-3.45倍区间）
            else if (ratio >= 2.55 && ratio <= 3.45)
            {
                double segmentHeight = block.Height / 3;
                newBlocks.Add(new Rect(block.X, block.Y, block.Width, (int)segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)( block.Y + segmentHeight), block.Width, (int)segmentHeight));
                newBlocks.Add(new Rect(block.X, (int)(block.Y + 2 * segmentHeight), block.Width, (int)segmentHeight));
            }
        }

        // 步骤3: 合并结果
        filtered.AddRange(newBlocks);
        return filtered;
    }

    public static List<Rect> FilterAndMergeBlocks(List<Rect> blocks)
    {
        if (blocks == null || blocks.Count == 0)
            return new List<Rect>();

        // 1. 初始过滤
        List<Rect> filtered = FilterBlocks(blocks);
        List<Rect> rejected = blocks.Except(filtered).ToList();

        if (rejected.Count == 0)
            return filtered;

        // 2. 计算参考尺寸（使用过滤后的块）
        var medianHeight = filtered.Select(b => b.Height).OrderBy(h => h).ElementAt(filtered.Count / 2);
        var medianWidth = filtered.Select(b => b.Width).OrderBy(w => w).ElementAt(filtered.Count / 2);
        var medianArea = filtered.Select(b => b.Width * b.Height).OrderBy(a => a).ElementAt(filtered.Count / 2);

        // 3. 合并参数（与FilterBlocks中一致）
        double minHeightRatio = 0.7;
        double maxHeightRatio = 1.3;
        double minWidthRatio = 0.33;
        double maxWidthRatio = 3.0;
        double minAreaRatio = 0.1;

        // 4. 尝试合并被过滤掉的块
        List<Rect> mergedBlocks = new List<Rect>();
        List<Rect> toRemove = new List<Rect>();

        foreach (var reject in rejected)
        {
            if (toRemove.Contains(reject)) continue;

            // 4.1 尝试与已保留的块合并
            bool merged = false;
            foreach (var keep in filtered)
            {
                if (CanMerge(reject, keep))
                {
                    Rect mergedRect = MergeRects(reject, keep);
                    if (IsValidBlock(mergedRect, medianHeight, medianWidth, medianArea,
                                   minHeightRatio, maxHeightRatio,
                                   minWidthRatio, maxWidthRatio,
                                   minAreaRatio))
                    {
                        mergedBlocks.Add(mergedRect);
                        toRemove.Add(reject);
                        merged = true;
                        break;
                    }
                }
            }

            if (merged) continue;

            // 4.2 尝试与其它被过滤的块合并
            foreach (var otherReject in rejected.Except(toRemove).Where(r => r != reject))
            {
                if (CanMerge(reject, otherReject))
                {
                    Rect mergedRect = MergeRects(reject, otherReject);
                    if (IsValidBlock(mergedRect, medianHeight, medianWidth, medianArea,
                                   minHeightRatio, maxHeightRatio,
                                   minWidthRatio, maxWidthRatio,
                                   minAreaRatio))
                    {
                        mergedBlocks.Add(mergedRect);
                        toRemove.Add(reject);
                        toRemove.Add(otherReject);
                        merged = true;
                        break;
                    }
                }
            }
        }

        // 5. 准备最终结果
        var finalResult = new List<Rect>(filtered);
        finalResult.AddRange(mergedBlocks);

        // 6. 移除被合并的块
        rejected = rejected.Except(toRemove).ToList();

        // 7. 最终过滤（确保合并后的块没有引入不符合条件的块）
        return FilterBlocks(finalResult);
    }

    // 判断两个矩形是否可以合并（有重叠或相邻）
    private static bool CanMerge(Rect a, Rect b)
    {
        // 扩展区域检测，增加合并可能性
        int mergeThreshold = 5; // 像素阈值
        Rect expandedA = new Rect(a.X - mergeThreshold, a.Y - mergeThreshold,
                                 a.Width + 2 * mergeThreshold, a.Height + 2 * mergeThreshold);

        return expandedA.IntersectsWith(b);
    }

    // 合并两个矩形
    private static Rect MergeRects(Rect a, Rect b)
    {
        int x1 = Math.Min(a.Left, b.Left);
        int y1 = Math.Min(a.Top, b.Top);
        int x2 = Math.Max(a.Right, b.Right);
        int y2 = Math.Max(a.Bottom, b.Bottom);

        return new Rect(x1, y1, x2 - x1, y2 - y1);
    }

    // 检查块是否符合条件
    private static bool IsValidBlock(Rect block, double medianHeight, double medianWidth, double medianArea,
                                   double minHeightRatio, double maxHeightRatio,
                                   double minWidthRatio, double maxWidthRatio,
                                   double minAreaRatio)
    {
        double heightRatio = (double)block.Height / medianHeight;
        double widthRatio = (double)block.Width / medianWidth;
        double areaRatio = (double)(block.Width * block.Height) / medianArea;

        return heightRatio >= minHeightRatio &&
               heightRatio <= maxHeightRatio &&
               widthRatio >= minWidthRatio &&
               widthRatio <= maxWidthRatio &&
               areaRatio >= minAreaRatio;
    }

    // 原有的FilterBlocks方法保持不变
    public static List<List<Rect>> GroupRectanglesByVerticalGap(List<Rect> rectangles, int gapThreshold = 30)
    {
        if (rectangles == null || rectangles.Count == 0)
            return new List<List<Rect>>();

        // 先按Y坐标排序
        var sortedRects = rectangles.OrderBy(r => r.Top).ToList();

        List<List<Rect>> groups = new List<List<Rect>>();
        List<Rect> currentGroup = new List<Rect> { sortedRects[0] };

        for (int i = 1; i < sortedRects.Count; i++)
        {
            // 计算当前矩形与前一个矩形的垂直间隔
            int verticalGap = sortedRects[i].Top - (currentGroup.Last().Top + currentGroup.Last().Height);

            // 如果间隔大于阈值，开始新的一组
            if (verticalGap > gapThreshold)
            {
                groups.Add(currentGroup);
                currentGroup = new List<Rect>();
            }

            currentGroup.Add(sortedRects[i]);
        }

        // 添加最后一组
        if (currentGroup.Count > 0)
        {
            groups.Add(currentGroup);
        }

        return groups;
    }

    public static List<Rect> TightenRects(Mat binaryImage, List<Rect> blocks)
    {
        List<Rect> tightenedRects = new List<Rect>();

        // 确保使用单通道图像
        Mat processImage = binaryImage;
        if (binaryImage.Channels() > 1)
        {
            processImage = new Mat();
            Cv2.CvtColor(binaryImage, processImage, ColorConversionCodes.BGR2GRAY);
        }

        foreach (Rect rect in blocks)
        {
            // 确保矩形在图像边界内
            int x = Math.Max(rect.X, 0);
            int y = Math.Max(rect.Y, 0);
            int right = Math.Min(rect.X + rect.Width, processImage.Cols);
            int bottom = Math.Min(rect.Y + rect.Height, processImage.Rows);
            int width = right - x;
            int height = bottom - y;

            if (width <= 0 || height <= 0) continue;

            Rect adjustedRect = new Rect(x, y, width, height);

            // 提取ROI并处理
            using (Mat roi = new Mat(processImage, adjustedRect))
            using (Mat invertedRoi = new Mat())
            {
                // 反相：使内容变为白色(255)，背景黑色(0)
                Cv2.BitwiseNot(roi, invertedRoi);

                // 查找轮廓
                Point[][] contours;
                HierarchyIndex[] hierarchy;
                Cv2.FindContours(
                    image: roi,   //本身已反相，引用原图
                    contours: out contours,
                    hierarchy: out hierarchy,
                    mode: RetrievalModes.External,
                    method: ContourApproximationModes.ApproxSimple
                );

                if (contours.Length == 0)
                {
                    tightenedRects.Add(adjustedRect);
                    continue;
                }

                // 初始化并合并所有轮廓的外接矩形
                Rect boundingRect = Cv2.BoundingRect(contours[0]);
                for (int i = 1; i < contours.Length; i++)
                {
                    Rect r = Cv2.BoundingRect(contours[i]);
                    boundingRect |= r;  // 使用并集运算符合并矩形
                }

                // 转换坐标到原图
                Rect finalRect = new Rect(
                    X: boundingRect.X + adjustedRect.X,
                    Y: boundingRect.Y + adjustedRect.Y,
                    Width: boundingRect.Width,
                    Height: boundingRect.Height
                );

                tightenedRects.Add(finalRect);
            }
        }

        // 如果创建了新的灰度图像，释放它
        if (processImage != binaryImage)
        {
            processImage.Dispose();
        }

        return tightenedRects;
    }

 
public static List<Rect> RefineRectangles(Mat src, List<Rect> inputRects)
{
    var results = new List<Rect>();

    foreach (var inputRect in inputRects)
    {
        // 确保矩形在图像范围内
        Rect rect = inputRect.Intersect(new Rect(0, 0, src.Width, src.Height));
        if (rect.Width <= 2 || rect.Height <= 2)
        {
            results.Add(inputRect);
            continue;
        }

        // 提取ROI区域
        using Mat roi = new Mat(src, rect);
        using Mat gray = new Mat();
        using Mat blurred = new Mat();
        using Mat binary = new Mat();

        // 转换为单通道灰度图
        if (roi.Channels() == 3)
            Cv2.CvtColor(roi, gray, ColorConversionCodes.BGR2GRAY);
        else if (roi.Channels() == 4)
            Cv2.CvtColor(roi, gray, ColorConversionCodes.BGRA2GRAY);
        else
            roi.CopyTo(gray);
     Mat roiA = new Mat();
            Cv2.BitwiseNot(gray, roiA);
        // 高斯模糊降噪
        Cv2.GaussianBlur(roiA, blurred, new Size(3, 3), 0);
          //  blurred.SaveImage("R:\\blurred20250728.007.JPG");
       
            // 使用Otsu阈值处理（更稳定可靠）
            double thresh = Cv2.Threshold(blurred, binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);

        // 形态学操作去除噪点
        using Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
           // kernel.SaveImage("R:\\kernel20250728.007.JPG");
            Cv2.MorphologyEx(binary, binary, MorphTypes.Close, kernel, iterations: 2);

        // 查找轮廓
        Point[][] contours;
        HierarchyIndex[] hierarchy;
        Cv2.FindContours(
            image: binary,
            contours: out contours,
            hierarchy: out hierarchy,
            mode: RetrievalModes.External,
            method: ContourApproximationModes.ApproxSimple
        );

        // 查找最大有效轮廓
        double maxArea = 0;
        Rect bestRect = new Rect();
        double minAreaThreshold = rect.Width * rect.Height * 0.05;

        foreach (var contour in contours)
        {
            double area = Cv2.ContourArea(contour);
            if (area > maxArea && area > minAreaThreshold)
            {
                maxArea = area;
                bestRect = Cv2.BoundingRect(contour);
            }
        }

        // 构建结果矩形
        Rect resultRect = maxArea > 0
            ? new Rect(
                X: rect.X + bestRect.X,
                Y: rect.Y + bestRect.Y,
                Width: bestRect.Width,
                Height: bestRect.Height)
            : inputRect;

        // 确保结果在图像范围内
        resultRect = resultRect.Intersect(new Rect(0, 0, src.Width, src.Height));
        results.Add(resultRect);
    }

    return results;
}

public static List<Rect> SubtractOverlappingRects(List<Rect> inputRects)
{
    List<Rect> result = new List<Rect>();
    List<Rect> coveredAreas = new List<Rect>();

    foreach (Rect currentRect in inputRects)
    {
        List<Rect> fragments = new List<Rect> { currentRect };

        // 用所有已覆盖区域裁剪当前矩形
        foreach (Rect covered in coveredAreas)
        {
            List<Rect> newFragments = new List<Rect>();

            foreach (Rect frag in fragments)
            {
                // 如果片段与覆盖区域无重叠，保留原片段
                if (!frag.IntersectsWith(covered))
                {
                    newFragments.Add(frag);
                    continue;
                }

                // 计算重叠区域
                Rect intersection = frag & covered;

                // 如果完全重叠则跳过
                if (intersection == frag) continue;

                // 将矩形分割成4个可能的部分
                // 1. 顶部区域
                if (frag.Top < intersection.Top)
                {
                    newFragments.Add(new Rect(frag.Left, frag.Top,
                                            frag.Width,
                                            intersection.Top - frag.Top));
                }

                // 2. 底部区域
                if (frag.Bottom > intersection.Bottom)
                {
                    newFragments.Add(new Rect(frag.Left, intersection.Bottom,
                                            frag.Width,
                                            frag.Bottom - intersection.Bottom));
                }

                // 3. 左侧区域
                if (frag.Left < intersection.Left)
                {
                    int top = intersection.Top > frag.Top ? intersection.Top : frag.Top;
                    int bottom = intersection.Bottom < frag.Bottom ? intersection.Bottom : frag.Bottom;
                    int height = bottom - top;

                    if (height > 0)
                    {
                        newFragments.Add(new Rect(frag.Left, top,
                                                intersection.Left - frag.Left,
                                                height));
                    }
                }

                // 4. 右侧区域
                if (frag.Right > intersection.Right)
                {
                    int top = intersection.Top > frag.Top ? intersection.Top : frag.Top;
                    int bottom = intersection.Bottom < frag.Bottom ? intersection.Bottom : frag.Bottom;
                    int height = bottom - top;

                    if (height > 0)
                    {
                        newFragments.Add(new Rect(intersection.Right, top,
                                                frag.Right - intersection.Right,
                                                height));
                    }
                }
            }

            fragments = newFragments;
            if (fragments.Count == 0) break;
        }

        // 添加剩余片段到结果
        result.AddRange(fragments);
        coveredAreas.AddRange(fragments);
    }

    return result;
}


    public static List<Rect> RemoveWhiteBordersFromRects(Mat src, List<Rect> rects, int noiseSize = 0)
    {
        var resultRects = new List<Rect>();
        
        // 处理每个矩形区域
        foreach (var rect in rects)
        {
            // 提取当前矩形区域的子图
            using var roi = new Mat(src, rect);
            //roi.SaveImage($"R:\\{Guid.NewGuid()}.PNG");
            // 创建白色阈值掩码（非白区域为255）
            using var mask = new Mat();
            Cv2.InRange(roi, new Scalar(250, 250, 250), new Scalar(255, 255, 255), mask);
            //Cv2.BitwiseNot(mask, mask);

            // 噪点处理：形态学开运算
            using var denoisedMask = new Mat();
            var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(1, 1));
            Cv2.MorphologyEx(mask, denoisedMask, MorphTypes.Open, kernel, iterations: 3);

            // 计算所有非零点的边界框（ROI坐标系）
            var points = denoisedMask.FindNonZero();

            if (points.Empty())
            {
                // 全白区域，保留原矩形
                resultRects.Add(rect);
            }
            else
            {
                // 获取内容边界框
                var contentRect = Cv2.BoundingRect(points);

                // 转换为原图坐标系
                var globalRect = new Rect(
                    X: rect.X + contentRect.X,
                    Y: rect.Y + contentRect.Y,
                    Width: contentRect.Width,
                    Height: contentRect.Height);

                resultRects.Add(globalRect);
            }
        }

        return resultRects;
    }


}

// 使用示例
//using (Mat src = Cv2.ImRead("document.jpg"))
//using (Mat result = ImageBlockProcessor.ProcessImageWithBlockNumbers(src))
//{
//    // 并排显示原图与结果
//    using Mat display = new Mat();
//    Cv2.HConcat(new[] { src, result }, display);

//    Cv2.ImShow("Processing Result", display);
//    Cv2.WaitKey();

//    // 保存结果
//    Cv2.ImWrite("processed_result.jpg", result);
//}